;(function () {
  /* 结构工具类型其实又可以分为两类，结构声明和结构处理。 */
  //-- Record<K extends keyof any, T> 的作用是将 K 中所有的属性的值转化为 T 类型。
  interface PageInfo {
    title: string
  }

  type Page = 'home' | 'about' | 'contact'
  type Page2 = Record<Page, PageInfo>

  const x: Page2 = {
    about: { title: 'about' },
    contact: { title: 'contact' },
    home: { title: 'home' }
  }
  //原理
  // type Record<K extends keyof any, T> = {
  //   [P in K]: T
  // }

  type Record2_1<K extends string | number | symbol, T> = { [P in K]: P }

  type test2 = Record2_1<Page, PageInfo>
  type test1 = 1 | 2 extends keyof any ? 1 : 2
  type test3 = 'home' | 'about' | 'contact' extends string | number | symbol ? 1 : 2

  // K extends keyof any 即为键的类型，这里使用 extends keyof any 标明，传入的 K 可以是单个类型，也可以是联合类型，而 T 即为属性的类型。
  type Record1 = Record<string, unknown> // 键名均为字符串，键值类型未知
  type Record2 = Record<string, any> // 键名均为字符串，键值类型任意
  type Record3 = Record<string | number, any> // 键名为字符串或数字，键值类型任意
  const test4: Record3 = {
    lys: '123',
    lys2: 123
  }

  //其中，Record<string, unknown> 和 Record<string, any> 是日常使用较多的形式，通常我们使用这两者来代替 object 。在一些工具类库源码中其实还存在类似的结构声明工具类型，如：
  type Dictionary<T> = {
    [index: string]: T
  }
  type NumbericDictionary<T> = {
    [index: number]: T
  }
  /* ------------------ */
  // Pick 从某个类型中挑出一些属性出来
  interface IUser1_pick {
    name: string
    age: number
    gender: number
  }
  //有一天我想要写一个新interface，它只拥有age、gender这两个属性，那我会重新写一个interface ,但其实使用Pick非常方便去做这件事
  type IUser2_pick = Pick<IUser1_pick, 'age' | 'gender'>

  //原理:Pick 从某个类型中挑出一些属性出来
  type Pick2<T, K extends keyof T> = {
    [P in K]: T[P]
  }
  /* ------------------ */
  //Exclude<T, U> 的作用是将某个类型中属于另一个的类型移除掉。
  type exclude0 = Exclude<'a' | 'b' | 'c', 'a'> // "b" | "c"
  type exclude1 = Exclude<'a' | 'b' | 'c', 'a' | 'b'> // "c"
  type exclude2 = Exclude<string | number | (() => void), Function> // string | number

  type test_exclude1 = 'a' | 'b' | 'c' extends 'a' ? 1 : 2
  // 原理：
  type Exclude1<T, U> = T extends U ? never : T

  /* ------------------ */
  interface IUser1_omit {
    name: string
    age: number
    gender: number
  }
  type IUser2 = Omit<IUser1_omit, 'name'> // 不要name
  type IUser3 = Omit<IUser1_omit, 'name1'> // 不要name1,你可能发现 Pick 会约束第二个参数的联合类型来自于对象属性，而 Omit 并不这么要求
  //原理：
  type Omit0<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>

  //
})()
//--另外，你可能发现 Pick 会约束第二个参数的联合类型来自于对象属性，而 Omit 并不这么要求？官方团队的考量是，可能存在这么一种情况：
type Omit1<T, K> = Pick<T, Exclude<keyof T, K>>
type Omit2<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>

// 这里就不能用严格 Omit 了
declare function combineSpread<T1, T2>(obj: T1, otherObj: T2, rest: Omit1<T1, keyof T2>): void

type Point3d = { x: number; y: number; z: number }
declare const p1: Point3d
// 能够检测出错误，rest 中缺少了 y
// combineSpread(p1, { x: 10 }, { z: 2 })

combineSpread(p1, { x: 10 }, { z: 2, y: 2 })
